/*=========================================================================

Program:   Virtual Surgery
Language:  C++
Date:      2009-9-1
Version:   0.1
Mail:		   nelfylee@gmail.com

Copyright (c) 2009 Nelfy Lee. All rights reserved.

=========================================================================*/
#ifndef _SMOOTHING_THREAD_H_
#define _SMOOTHING_THREAD_H_

#include "VolumeSource.h"
#include "FilteringThreadBase.h"

#include "itkCastImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkCurvatureFlowImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkAntiAliasBinaryImageFilter.h"
#include "itkCurvatureAnisotropicDiffusionImageFilter.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryBallStructuringElement.h"
#include "itkBinaryMorphologicalClosingImageFilter.h"
#include "itkBinaryMorphologicalOpeningImageFilter.h"
#include "itkGrayscaleFillHoleImageFilter.h"

template<class TImageType>
class ItkCurvatureFlowThread : public FilteringThreadBase
{
	typedef itk::CurvatureFlowImageFilter<TImageType, ImageFloat3D> FilterType;
	typedef itk::RescaleIntensityImageFilter<ImageFloat3D, ImageShort3D> RescaleFilterType;

public:
	ItkCurvatureFlowThread(Source *source, int smoothLevel):FilteringThreadBase(source)
	{
		m_level = smoothLevel;
	}
	~ItkCurvatureFlowThread(){}

protected:
	void run()
	{
		emit emitProgressStart(QString("Vessel Smoothing..."));

		int iteration = 0;
		QString level;

		if ( m_level == 0)
		{
			iteration = 3;
			level = QString("L");
		}
		else if ( m_level == 1)
		{
			iteration = 10;
			level = QString("M");
		}
		else if ( m_level == 2)
		{
			level = QString("H");
			iteration = 20;
		}

		typename FilterType::Pointer filter = FilterType::New();
		filter->SetInput(((VolumeSource<TImageType>*)m_source)->getVolume());
		filter->SetNumberOfIterations(iteration);

		if ( m_source->dim()==3)
			filter->SetTimeStep(0.0625);
		else
			filter->SetTimeStep(0.125);

		ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();
		progressCmd->SetCallbackFunction( this, &ItkCurvatureFlowThread::ProcessEvent );

		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->SetReleaseDataFlag(true);
		filter->Update();

		VolumeSource<ImageFloat3D> *subsource = 
			new VolumeSource<ImageFloat3D>(QString("V-Smoothed-%1").arg(level));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);

		emit emitProgressDone();
	}

private:
	int m_level;
};



template<class TImageType>
class ItkCurvatureAnisotropicDiffusionThread : public FilteringThreadBase
{
	typedef itk::CurvatureAnisotropicDiffusionImageFilter<TImageType, ImageFloat3D> FilterType;

public:
	ItkCurvatureAnisotropicDiffusionThread(Source *source, int smoothLevel):FilteringThreadBase(source)
	{
		m_level = smoothLevel;
		m_cond = 9.0;
	}
	~ItkCurvatureAnisotropicDiffusionThread(){}

protected:
	void run()
	{
		emit emitProgressStart(QString("Liver Smoothing..."));

		int iteration = 0;
		QString level;

		if ( m_level == 0)
		{
			iteration = 2;
			level = QString("L");
		}
		else if ( m_level == 1)
		{
			iteration = 5;
			level = QString("M");
		}
		else if ( m_level == 2)
		{
			level = QString("H");
			iteration = 10;
		}

		typename FilterType::Pointer filter = FilterType::New();
		filter->SetInput(((VolumeSource<TImageType>*)m_source)->getVolume());
		filter->SetNumberOfIterations(iteration);
		filter->SetConductanceParameter(m_cond);

		if ( m_source->dim()==3)
			filter->SetTimeStep(0.0625);
		else
			filter->SetTimeStep(0.125);

		ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();
		progressCmd->SetCallbackFunction( this, &ItkCurvatureAnisotropicDiffusionThread::ProcessEvent );

		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();

		VolumeSource<ImageFloat3D> *subsource = 
			new VolumeSource<ImageFloat3D>(QString("Li-Smoothed-%1").arg(level));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);

		emit emitProgressDone();
	}


private:
	int m_level;
	double m_cond;
};





template<class TImageType>
class ItkAntiAliasBinaryThread : public FilteringThreadBase
{
	typedef itk::Image<float, 3> RealImageType;
	typedef itk::CastImageFilter<TImageType, RealImageType> CastToRealFilterType;
	typedef itk::AntiAliasBinaryImageFilter<RealImageType, RealImageType> FilterType;
	typedef itk::RescaleIntensityImageFilter<RealImageType, ImageUChar3D> RescaleFilter;

public:
	ItkAntiAliasBinaryThread(Source *source, int smoothLevel):FilteringThreadBase(source)
	{
		m_level = smoothLevel;
		m_rms = 0.01;
	}
	~ItkAntiAliasBinaryThread(){}

protected:
	void run()
	{
		emit emitProgressStart(QString("Post Smoothing..."));

		int iteration = 0;
		QString level;

		if ( m_level == 0)
		{
			iteration = 2;
			level = QString("L");
		}
		else if ( m_level == 1)
		{
			iteration = 10;
			level = QString("M");
		}
		else if ( m_level == 2)
		{
			level = QString("H");
			iteration = 20;
		}

		typename CastToRealFilterType::Pointer toReal = CastToRealFilterType::New();
		typename RescaleFilter::Pointer rescale = RescaleFilter::New();
		typename FilterType::Pointer filter = FilterType::New();

		typename ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();
		progressCmd->SetCallbackFunction( this, &ItkAntiAliasBinaryThread::ProcessEvent );

		toReal->SetInput(((VolumeSource<TImageType>*)m_source)->getVolume());
		toReal->Update();

		filter->SetInput(toReal->GetOutput());
		filter->SetNumberOfIterations(iteration);
		filter->SetMaximumRMSError(m_rms);
		filter->SetNumberOfLayers(2);
		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();

		rescale->SetInput(filter->GetOutput());
		rescale->Update();


		VolumeSource<ImageUChar3D> *subsource = 
			new VolumeSource<ImageUChar3D>(QString("P-Smoothed-%1").arg(level));
		subsource->setVolume(rescale->GetOutput());

		m_source->addChild(subsource);

		emit emitProgressDone();
	}


private:
	int m_level;
	double m_rms;
};



enum BinaryMorph
{
	bmDilation = 0,
	bmErosion,
	bmOpening,
	bmClosing
};



template<class CInputImageType>
class BinaryMorphologicalThread : public FilteringThreadBase
{
	//typedef itk::Image<unsigned char, 3> ImageUChar3D;
	//typedef itk::CastImageFilter<CInputImageType, ImageUChar3D> CastImageFilter;
	typedef itk::BinaryBallStructuringElement<typename CInputImageType::PixelType,3> StructuringElementType;
	typedef itk::BinaryErodeImageFilter<CInputImageType,CInputImageType,StructuringElementType> BEFilterType;
	typedef itk::BinaryDilateImageFilter<CInputImageType,CInputImageType,StructuringElementType> BDFilterType;
	typedef itk::BinaryMorphologicalClosingImageFilter<CInputImageType,CInputImageType,StructuringElementType> BCFilterType;
	typedef itk::BinaryMorphologicalOpeningImageFilter<CInputImageType,CInputImageType,StructuringElementType> BOpeningFilterType;

public:
	BinaryMorphologicalThread(Source *source):FilteringThreadBase(source)
	{
// 		CastImageFilter::Pointer caster = CastImageFilter::New();
// 		caster->SetInput(((VolumeSource<CInputImageType>*)m_source)->getVolume());
// 		caster->Update();
// 		m_inputImage = caster->GetOutput();
		m_inputImage = ((VolumeSource<CInputImageType>*)m_source)->getVolume();
	}

	~BinaryMorphologicalThread(){}

	void setParameters(BinaryMorph mode, int foreground, int background = 0, int radius = 1)
	{
		m_mode = mode;
		m_foreground = foreground;
		m_background = background;
		m_radius = radius;
	}

protected:
	void run()
	{
		emit emitProgressStart(QString("Morphing..."));

		switch (m_mode)
		{
		case bmErosion:
			runErosion();
			break;
		case bmDilation:
			runDilation();
			break;
		case bmOpening:
			runOpening();
			break;
		case bmClosing:
			runClosing();
			break;
		default:
			break;
		}

		emit emitProgressDone();
	}


	void runErosion()
	{
		typename BEFilterType::Pointer filter = BEFilterType::New();
		typename ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();

		progressCmd->SetCallbackFunction( this, &BinaryMorphologicalThread::ProcessEvent );

		StructuringElementType structuringElement;
		structuringElement.SetRadius(m_radius);
		structuringElement.CreateStructuringElement();


		filter->SetKernel(structuringElement);
		filter->SetInput(m_inputImage);
		filter->SetErodeValue(m_foreground);
		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();


		VolumeSource<CInputImageType> *subsource = 
			new VolumeSource<CInputImageType>(QString("Erosion#%1").arg(m_source->rowCount()+1));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);
	}


	void runDilation()
	{
		typename BDFilterType::Pointer filter = BDFilterType::New();
		typename ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();

		progressCmd->SetCallbackFunction( this, &BinaryMorphologicalThread::ProcessEvent );

		StructuringElementType structuringElement;
		structuringElement.SetRadius(m_radius);
		structuringElement.CreateStructuringElement();


		filter->SetKernel(structuringElement);
		filter->SetInput(m_inputImage);
		filter->SetDilateValue(m_foreground);
		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();


		VolumeSource<CInputImageType> *subsource = 
			new VolumeSource<CInputImageType>(QString("Dilation#%1").arg(m_source->rowCount()+1));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);
	}


	void runOpening()
	{
		typename BOpeningFilterType::Pointer filter = BOpeningFilterType::New();
		typename ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();

		progressCmd->SetCallbackFunction( this, &BinaryMorphologicalThread::ProcessEvent );

		StructuringElementType structuringElement;
		structuringElement.SetRadius(m_radius);
		structuringElement.CreateStructuringElement();


		filter->SetKernel(structuringElement);
		filter->SetInput(m_inputImage);
		filter->SetForegroundValue(m_foreground);
		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();


		VolumeSource<CInputImageType> *subsource = 
			new VolumeSource<CInputImageType>(QString("Opening#%1").arg(m_source->rowCount()+1));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);
	}


	void runClosing()
	{
		typename BCFilterType::Pointer filter = BCFilterType::New();
		typename ProgressCommandType::Pointer progressCmd = ProgressCommandType::New();

		progressCmd->SetCallbackFunction( this, &BinaryMorphologicalThread::ProcessEvent );

		StructuringElementType structuringElement;
		structuringElement.SetRadius(m_radius);
		structuringElement.CreateStructuringElement();


		filter->SetKernel(structuringElement);
		filter->SetInput(m_inputImage);
		filter->SetForegroundValue(m_foreground);
		filter->AddObserver( itk::ProgressEvent(), progressCmd.GetPointer());
		filter->Update();


		VolumeSource<CInputImageType> *subsource = 
			new VolumeSource<CInputImageType>(QString("Closing#%1").arg(m_source->rowCount()+1));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);
	}

private:
	int m_radius;
	int m_foreground;
	int m_background;
	BinaryMorph m_mode;
	typename CInputImageType::Pointer m_inputImage;
};







template<class CInputImage>
class ItkFillHoleThread : public FilteringThreadBase
{
	typedef itk::GrayscaleFillholeImageFilter<CInputImage, CInputImage> GrayscaleFillholeImageFilter;

public:
	ItkFillHoleThread(Source *source):FilteringThreadBase(source)
	{
		m_inputImage = ((VolumeSource<CInputImage>*)m_source)->getVolume();

	}
	~ItkFillHoleThread(){}

protected:
	void run()
	{
		emit emitProgressStart(QString("Filling holes..."));

		typename GrayscaleFillholeImageFilter::Pointer filter = GrayscaleFillholeImageFilter::New();

		filter->SetInput(m_inputImage);
		//filter->FullyConnectedOn();
		filter->Update();

		VolumeSource<CInputImage> *subsource = 
			new VolumeSource<CInputImage>(QString("FillHole#%1").arg(m_source->rowCount()+1));
		subsource->setVolume(filter->GetOutput());

		m_source->addChild(subsource);

		emit emitProgressDone();
	}
private:
	typename CInputImage::Pointer m_inputImage;
};





#endif


